﻿using Devil;
using System.Collections.Generic;
using System.Reflection;
using TableCore.Plugin;

namespace TableCore
{
    public class Factory
    {
        public static readonly object sync_lock = new object();
        static Factory _inst;
        public static Factory GetInstance()
        {
            lock(sync_lock)
            {
                if (_inst == null)
                    _inst = new Factory();
                return _inst;
            }
        }

        public static string LibFolder
        {
            get
            {
                var inst = _inst;
                if (inst != null)
                    return inst.mLibFolder;
                else
                    return null;
            }
            set
            {
                var inst = GetInstance();
                if(inst != null)
                {
                    inst.mLibFolder = value;
                }
            }
        }

        string mLibFolder;
        Dictionary<string, GTType> mTypes;
        ParallelDispatcher.CombinedHandler mInitialize;
        ParallelDispatcher.Handler mPrepareCmd;
        Dictionary<string, System.Type> mFormatterTypes;

        private Factory()
        {
            mTypes = new Dictionary<string, GTType>();
            mInitialize = new ParallelDispatcher.CombinedHandler("Init GTType");
            mFormatterTypes = new Dictionary<string, System.Type>();
            mPrepareCmd = ParallelUtils.Schedule(LoadFormatters, false);
            mInitialize.Combine(mPrepareCmd);
        }

        public ParallelDispatcher.Handler GetInitializeHandler()
        {
            return mInitialize.GetHandler();
        }

        bool IsInterface(System.Type type, System.Type interfaceType)
        {
            var inters = type.GetInterfaces();
            if (inters == null)
                return false;
            for (int i = 0; i < inters.Length; i++)
            {
                if (inters[i] == interfaceType)
                    return true;
            }
            return false;
        }

        void LoadFormatters()
        {
            var assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
            var intype = typeof(IGenFormatter);
            var gttype = typeof(GTType);
            foreach (var asmb in assemblies)
            {
                var types = asmb.GetTypes();
                foreach (var type in types)
                {
                    if (!type.IsClass || type.IsAbstract || type == gttype || !IsInterface(type, intype))
                        continue;
                    mFormatterTypes[type.FullName] = type;
                    if (!mFormatterTypes.ContainsKey(type.Name))
                        mFormatterTypes[type.Name] = type;
                    var att = type.GetCustomAttributes<FormatterAttribute>();
                    if (att == null)
                        continue;
                    foreach (var alias in att)
                    {
                        if (!string.IsNullOrEmpty(alias.Alias))
                            mFormatterTypes[alias.Alias] = type;
                    }
                }
            }
        }

        public System.Type GetFormatter(string formatterName)
        {
            System.Type tp;
            if (mFormatterTypes.TryGetValue(formatterName, out tp))
                return tp;
            else
                return null;
        }

        public void PrintFormatters()
        {
            if (mPrepareCmd.IsValid)
            {
                Debug.LogError("The factory is not initialized complete.");
                return;
            }
            var buf = ParallelUtils.GetBuilder();
            buf.Append("Formatters >").NextLine();
            foreach (var kv in mFormatterTypes)
            {
                var name = kv.Value.FullName;
                if (kv.Key == name)
                    continue;
                buf.AppendTab(1).Append(kv.Key).Append(": ").Append(name).NextLine();
            }
            Debug.Log(ParallelUtils.ReturnRelease(buf));
        }

        public GTType GetGenType(string typeName)
        {
            if (string.IsNullOrEmpty(typeName))
                return null;
            bool init = false;
            GTType tp = null;
            lock(sync_lock)
            {
                if(!mTypes.TryGetValue(typeName, out tp))
                {
                    init = true;
                    tp = new GTType(typeName);
                    mTypes[typeName] = tp;
                }
            }
            if(init)
            {
                System.Action initer = tp.Initialize;
                mInitialize.Combine(initer.Schedule(false, mPrepareCmd));
            }
            return tp;
        }
    }
}
